package org.ccay.ws.service.impl;

import java.io.IOException;
import java.security.InvalidParameterException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.ccay.core.exception.ApplicationException;
import org.ccay.core.exception.impl.CommonApplicationException;
import org.ccay.core.log.CcayLoggerFactory;
import org.ccay.core.log.ILogger;
import org.ccay.core.orm.NoTransation;
import org.ccay.core.server.IServerListService;
import org.ccay.core.server.ServerInfoVO;
import org.ccay.core.util.HttpUtil;
import org.ccay.core.util.JsonUtil;
import org.ccay.ws.service.IClusterInvokeService;

/**
 * IClusterInvokeService 默认实现
 */
@Named
public class ClusterInvokeService implements IClusterInvokeService {
	private static final ILogger logger = CcayLoggerFactory.getLogger(ClusterInvokeService.class);
	
	//方法get的key
	public static final String GET_METHOD = "GET";
	
	//方法out的key
	public static final String PUT_METHOD = "PUT";
	
	//方法post的key
	public static final String POST_METHOD = "POST";
	
	//方法delete的key
	public static final String DELETE_METHOD = "DELETE";
	
	//服务url的key
	public static final String SERVICE_URI = "SERVICE_URI";
	
	//服务方法的key
	public static final String SERVICE_METHOD = "SERVICE_METHOD";
	
	//服务参数的key
	public static final String SERVICE_QUERYPARAMETERS = "SERVICE_QUERYPARAMETERS";
	
	//服务其他参数的key
	public static final String SERVICE_OTHERPARAMETERS = "SERVICE_OTHERPARAMETERS";
	

	
	/**
	 * 服务器列表服务
	 */
	@Inject
	private IServerListService serverListService;
	/* (non-Javadoc)
	 * @see www.ccay.org.ccay.web.support.service.IClusterInvokeService#invoke(java.lang.String, java.lang.String)
	 */
	@NoTransation
	public void invoke(String uri, String queryParameters) throws ApplicationException {
		this.invoke(uri, GET_METHOD, queryParameters, null);
	}
	
	/**
	 * 包含其他post 或者put参数的访问
	 * @param uri
	 * @param queryParameters
	 * @param otherParameters
	 * @throws ApplicationException
	 * Created：2013-4-3
	 * @author chaowangbang
	 */
	@NoTransation
	public void invoke(String uri,String method, String queryParameters,Object otherParameters) throws ApplicationException {
		//获取服务器列表
		List<ServerInfoVO> serverList = serverListService.findServerList();		
		/**
		 * 出错的服务器
		 */
		Map<String, String> failedServerList = new LinkedHashMap<String, String>();
		/**
		 * 组装一个服务map  装 uri  mehtod parame
		 */
		Map<String, Object> serverInfoMap = new LinkedHashMap<String, Object>();
		serverInfoMap.put(SERVICE_URI, uri);
		serverInfoMap.put(SERVICE_METHOD, method);
		serverInfoMap.put(SERVICE_QUERYPARAMETERS, queryParameters);
		serverInfoMap.put(SERVICE_OTHERPARAMETERS, otherParameters);
		this.checkServerInfo(serverInfoMap);
		for(ServerInfoVO server : serverList){
			invokeOneServer(serverInfoMap, failedServerList, server);
		}
		if(failedServerList.isEmpty()){
			return ;
		}
		StringBuilder returnError = new StringBuilder();
		for(Map.Entry<String, String>  entry : failedServerList.entrySet()){
			returnError.append(entry.getKey());
			returnError.append(": ");
			returnError.append(entry.getValue());
			returnError.append(".\n");
		}
		throw new CommonApplicationException(returnError.toString());
	}

	/**
	 * 校验获取的服务信息是否正确
	 * @param serverInfo
	 * @throws InvalidParameterException
	 * Created：2013-5-10
	 * @author chaowangbang
	 */
	private void checkServerInfo(Map<String, Object>  serverInfo) throws InvalidParameterException{
		/**如果url  和 method 为空 那么直接异常**/
		if(null == serverInfo.get(SERVICE_URI) ){
			logger.error("invoke server uri is null.");
			throw new InvalidParameterException();
		}
		if(null == serverInfo.get(SERVICE_METHOD) ){
			logger.error("invoke server method is null.");
			throw new InvalidParameterException();
		}
	}
	/** 
	 * 调用一个服务器
	 * @param uri
	 * @param queryParameters
	 * @param failedServerList
	 * @param server
	 */
	private void invokeOneServer(Map<String, Object> serverInfoMap, Map<String, String> failedServerList,
			ServerInfoVO server) {
		try {
			logger.debug("Before invoke server:" + server.getServerName() +" for service:" + serverInfoMap.get(SERVICE_URI).toString());
			int statusCode = invokeServer(server,serverInfoMap);

			if(!HttpUtil.isNormalResponse(statusCode)){
				failedServerList.put(server.getServerName(), "Server return http code " +statusCode);
			}
		} catch(ApplicationException ex){
			logger.error(ex);
			failedServerList.put(server.getServerName(), ex.getMessage());
		}  catch (HttpException ex) {
			logger.error(ex);
			failedServerList.put(server.getServerName(), ex.getMessage());
		} catch (IOException ex) {
			logger.error(ex);
			failedServerList.put(server.getServerName(), ex.getMessage());
		} catch (RuntimeException ex){
			logger.error(ex);
			failedServerList.put(server.getServerName(), ex.getMessage());
		} 
	}
	
	/** 
	 * 调用某服务器
	 * @param server
	 * @param uri
	 * @param queryParameters
	 * @throws HttpException
	 * @throws IOException
	 * @throws CommonApplicationException 
	 */
	private int invokeServer(ServerInfoVO server,Map<String, Object> serverInfoMap) throws HttpException, IOException, CommonApplicationException{
//		String urlPattern="http://{0}:{1,number,#}{2}{3}";
//		String contextPath= RequestContext.getCurrent().getApplication().getContextPath();
//		//构建URL
//		String url  = MessageFormat.format(urlPattern, server.getServerDomainName(),server.getAppPort(),contextPath,serverInfoMap.get(SERVICE_URI).toString());
		String url = "http://" + server.getServerUrl() + serverInfoMap.get(SERVICE_URI).toString(); 
		logger.debug("Invoke server url : {0}", url);
		
		//其他情况  使用get方式
		HttpMethod methodObj = new GetMethod(url);
		String  method = serverInfoMap.get(SERVICE_METHOD).toString();
		//如果数据库对象是空的 那么使用get方式传递
		if(GET_METHOD.equalsIgnoreCase(method)){
			methodObj = new GetMethod(url);
			//处理put参数
		}else if(PUT_METHOD.equalsIgnoreCase(method)){	
			PutMethod putMethod = new PutMethod(url);
			putMethod.setRequestEntity(this.getRequestEntity(serverInfoMap.get(SERVICE_OTHERPARAMETERS)));
			methodObj = putMethod;
			//处理post参数
		}else if(POST_METHOD.equalsIgnoreCase(method)){
			PostMethod postMethod = new PostMethod(url);
			postMethod.setRequestEntity(this.getRequestEntity(serverInfoMap.get(SERVICE_OTHERPARAMETERS)));
			methodObj = postMethod;
		}else if(DELETE_METHOD.equalsIgnoreCase(method)){
			methodObj = new DeleteMethod(url);
		}
		//methodObj.addRequestHeader(SecurityConstants.INTERNAL_TOKEN, SecurityUtil.genInternalToken());
		
		if(serverInfoMap.get(SERVICE_QUERYPARAMETERS) != null){
			methodObj.setQueryString(serverInfoMap.get(SERVICE_QUERYPARAMETERS).toString());
		}
		HttpClient client = new HttpClient();
		try{
			return client.executeMethod(methodObj); 
		}finally{
			methodObj.releaseConnection();
		}		
	}
	
	private RequestEntity getRequestEntity(Object otherParameters) throws IOException{
		if(null == otherParameters){
			return null;
		}
		//其他参数 使用json数据传递
		String jsonData = JsonUtil.getJsonString(otherParameters);
		RequestEntity  jsonEntity = new StringRequestEntity(jsonData,"application/json","utf-8");
		return  jsonEntity;
	}
}
